﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Pyramid
{
    class Faber:Multiresolution
    {
        public override ImageWithDetail DownSample(ImageWithDetail dimage)
        {
            int i, j;
            ImageWithDetail rimage = new ImageWithDetail(dimage);
            GImage image = rimage.TheImage;
            double[] r = new double[image.Depth];
            GImage FinalImage = new GImage(image.Width / 2, image.Height / 2);
            GImage DetailImage1 = new GImage(image.Width / 2, image.Height / 2);
            GImage DetailImage2 = new GImage(image.Width / 2, image.Height / 2);
            GImage DetailImage3 = new GImage(image.Width / 2, image.Height / 2);


            //apply the filter to the columns
            GImage RowImage = new GImage(image.Width / 2, image.Height);

            for (j = 0; j < image.Height; j++)
            {



                for (i = 0; i < image.Width; i += 2)
                {
                    //find the lower resulotion pixel value
                    r = Find_NewValue(image, new double[5] { -1.0 / 6, 1.0 / 3, 2.0 / 3, 1.0 / 3, -1.0 / 6 }, new int[2] { i, j }, 0);
                    RowImage.SetPixel(i / 2, j, r);

                }

            }


            //Find the details
            GImage RowDetailImage = new GImage(image.Width / 2, image.Height);
            for (j = 0; j < image.Height; j++)
            {


                for (i = 0; i < image.Width; i += 2)
                {
                    //find the corresponding detail
                    r = Find_NewValue(image, new double[3] { -0.5, 1.0, -0.5 }, new int[2] { i, j }, 0,1);
                    RowDetailImage.SetPixel(i / 2, j, r);

                }

            }


            //reduce the resolution in vertical direction
            for (i = 0; i < RowImage.Width; i++)
            {




                for (j = 0; j < RowImage.Height; j += 2)
                {
                    //find the lower resulotion pixel value
                    //the row image
                    r = Find_NewValue(RowImage, new double[5] { -1.0 / 6, 1.0 / 3, 2.0 / 3, 1.0 / 3, -1.0 / 6 }, new int[2] { i, j }, 1);
                    FinalImage.SetPixel(i, j / 2, r);
                    //the row detail image
                    r = Find_NewValue(RowDetailImage, new double[5] { -1.0 / 6, 1.0 / 3, 2.0 / 3, 1.0 / 3, -1.0 / 6 }, new int[2] { i, j }, 1);
                    DetailImage2.SetPixel(i, j / 2, r);


                }

            }

            //Find the details

            for (i = 0; i < RowImage.Width; i++)
            {


                for (j = 0; j < RowImage.Height; j += 2)
                {
                    //find the corresponding detail
                    //the row image
                    r = Find_NewValue(RowImage, new double[3] { -0.5, 1.0, -0.5 }, new int[2] { i, j }, 1,1);
                    DetailImage1.SetPixel(i, j / 2, r);
                    //the row detail image
                    r = Find_NewValue(RowDetailImage, new double[3] { -0.5, 1.0, -0.5 }, new int[2] { i, j }, 1,1);
                    DetailImage3.SetPixel(i, j / 2, r);


                }

            }



            rimage.TheImage = FinalImage;



            //save the details
            //if (AmplifyDetails) Amp_Factor = 5;
            rimage.TheDetails.Add(Amp_Factor * DetailImage1);//the detail of the final image
            rimage.TheDetails.Add(Amp_Factor * DetailImage2);//the detail image of the row image
            rimage.TheDetails.Add(Amp_Factor * DetailImage3);//the detail of the detail image

            return rimage;
        }
        public override ImageWithDetail UpSample(ImageWithDetail dimage)
        {
            int i, j;
            double[] r1 = new double[dimage.TheImage.Depth];
            double[] r2 = new double[dimage.TheImage.Depth];
            GImage image = dimage.TheImage;
            GImage FinalImage = new GImage(image.Width * 2, image.Height * 2);

            //increase the resolution in horizontal direction
            GImage RowImage = new GImage(image.Width, image.Height * 2);
            GImage RowDetailImage = new GImage(image.Width, image.Height * 2);

            for (i = 0; i < image.Width; i++)
            {
            
                //perfect reconstruction
                //f1=c1+2/3d1-1/3d2
                for (int k = 0; k < image.Depth; k++)
                    r1[k] = image.data[i, 0, k];

                if (dimage.TheDetails.Count > 0)
                    for (int k = 0; k < image.Depth; k++)
                        r2[k] = (2.0 / 3) * dimage.TheDetails[dimage.TheDetails.Count - 3].data[i, 0, k] - (1.0 / 3) * dimage.TheDetails[dimage.TheDetails.Count - 3].data[i, 1, k];
                else
                {
                    for (int k = 0; k < r2.Length; k++)
                        r2[k] = 0;
                }
                for (int k = 0; k < r2.Length; k++)
                    r1[k] += r2[k];
                RowImage.SetPixel(i, 0, r1);

                //do the same for the detail image if any
                if (dimage.TheDetails.Count > 0)
                {
                    //perfect reconstruction
                    //f1=c1+2/3d1-1/3d2
                    for (int k = 0; k < image.Depth; k++)
                        r1[k] = dimage.TheDetails[dimage.TheDetails.Count - 2].data[i, 0, k];

                    if (dimage.TheDetails.Count > 0)
                        for (int k = 0; k < image.Depth; k++)
                            r2[k] = (2.0 / 3) * dimage.TheDetails[dimage.TheDetails.Count - 1].data[i, 0, k] - (1.0 / 3) * dimage.TheDetails[dimage.TheDetails.Count - 1].data[i, 1, k];
                    else
                    {
                        for (int k = 0; k < r2.Length; k++)
                            r2[k] = 0;
                    }
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] += r2[k];
                    RowDetailImage.SetPixel(i, 0, r1);
                }
                else
                {
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] = 0;

                    RowDetailImage.SetPixel(i, 0, r1);

                }
                //the regular filters
                for (j = 0; j < image.Height-1; j++)
                {

                    for (int k = 0; k < image.Depth; k++)
                        r1[k] = image.data[i, j, k];
                    
                    if (dimage.TheDetails.Count > 0)
                        r2 = Find_NewValue(dimage.TheDetails[dimage.TheDetails.Count - 3], new double[2] { -1.0/3,-1.0/3}, new int[2] { i, j }, 1);
                    else
                    {
                        for (int k = 0; k < r2.Length; k++)
                            r2[k] = 0;
                    }
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] += r2[k] * (-2.0/3.0);
                    RowImage.SetPixel(i, 2 * j+1 , r1);

                    r1 = Find_NewValue(image, new double[2] { 0.5, 0.5 }, new int[2] { i, j }, 1);
                    if (dimage.TheDetails.Count > 0)
                        r2 = Find_NewValue(dimage.TheDetails[dimage.TheDetails.Count - 3], new double[3] { -1.0/6,2.0/3, -1.0/6 }, new int[2] { i, j }, 1);
                    else
                    {
                        for (int k = 0; k < r2.Length; k++)
                            r2[k] = 0;
                    }
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] += (r2[k] /3.0);
                    RowImage.SetPixel(i, 2 * j + 2, r1);
                    if (dimage.TheDetails.Count > 0)
                    {
                        //do the same to find the detail row image
                        for (int k = 0; k < image.Depth; k++)
                            r1[k] = dimage.TheDetails[dimage.TheDetails.Count - 2].data[i, j, k];
                        
                        r2 = Find_NewValue(dimage.TheDetails[dimage.TheDetails.Count - 1], new double[2] {-1.0/3,-1.0/3 }, new int[2] { i, j }, 1);
                        for (int k = 0; k < r2.Length; k++)
                            r1[k] += r2[k] *(-2.0/ 3.0);

                        RowDetailImage.SetPixel(i, 2 * j +1, r1);


                        r1 = Find_NewValue(dimage.TheDetails[dimage.TheDetails.Count - 2], new double[2] { 0.5, 0.5 }, new int[2] { i, j }, 1);
                        r2 = Find_NewValue(dimage.TheDetails[dimage.TheDetails.Count - 1], new double[3] { -1.0 / 6, 2.0 / 3, -1.0 / 6 }, new int[2] { i, j }, 1);
                        for (int k = 0; k < r2.Length; k++)
                            r1[k] += r2[k] /3.0;
                        RowDetailImage.SetPixel(i, 2 * j + 2, r1);
                    }
                    else
                    {
                        for (int k = 0; k < r2.Length; k++)
                            r1[k] = 0;

                        RowDetailImage.SetPixel(i, 2 * j +1, r1);
                        RowDetailImage.SetPixel(i, 2 * j + 2, r1);
                    }

                }

                //perfect reconstruction
                //fn=cn-2/3dn
                for (int k = 0; k < image.Depth; k++)
                    r1[k] = image.data[i, image.Height - 1, k];

                if (dimage.TheDetails.Count > 0)
                    for (int k = 0; k < image.Depth; k++)
                        r2[k] = (-2.0/3)*dimage.TheDetails[dimage.TheDetails.Count - 3].data[i, dimage.TheDetails[dimage.TheDetails.Count - 3].Height - 1, k];
                else
                {
                    for (int k = 0; k < r2.Length; k++)
                        r2[k] = 0;
                }
                for (int k = 0; k < r2.Length; k++)
                    r1[k] += r2[k];
                RowImage.SetPixel(i, RowImage.Height - 1, r1);

                //do the same for the detail image if any
                if (dimage.TheDetails.Count > 0)
                {
                    //perfect reconstruction
                    //f1=cn-2/3dn
                    for (int k = 0; k < image.Depth; k++)
                        r1[k] = dimage.TheDetails[dimage.TheDetails.Count - 2].data[i, dimage.TheDetails[dimage.TheDetails.Count - 2].Height - 1, k];

                    if (dimage.TheDetails.Count > 0)
                        for (int k = 0; k < image.Depth; k++)
                            r2[k] = (-2.0/3)*dimage.TheDetails[dimage.TheDetails.Count - 1].data[i, dimage.TheDetails[dimage.TheDetails.Count - 1].Height - 1, k];
                    else
                    {
                        for (int k = 0; k < r2.Length; k++)
                            r2[k] = 0;
                    }
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] += r2[k];
                    RowDetailImage.SetPixel(i, RowDetailImage.Height - 1, r1);
                }
                else
                {
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] = 0;

                    RowDetailImage.SetPixel(i, RowDetailImage.Height - 1, r1);

                }
            }
            //increase the resolution in vertical direction
            for (j = 0; j < RowImage.Height; j++)
            {

                //perfect reconstruction
                //f1=c1+2/3d1-1/3d2
                for (int k = 0; k < image.Depth; k++)
                    r1[k] = RowImage.data[0, j, k];

                if (dimage.TheDetails.Count > 0)
                    for (int k = 0; k < image.Depth; k++)
                        r2[k] = (2.0 / 3) * RowDetailImage.data[0, j, k] - (1.0 / 3) * RowDetailImage.data[1, j, k];
                else
                {
                    for (int k = 0; k < r2.Length; k++)
                        r2[k] = 0;
                }
                for (int k = 0; k < r2.Length; k++)
                    r1[k] += r2[k];
                FinalImage.SetPixel(0, j, r1);
                //regular filters
                for (i = 0; i < RowImage.Width-1; i++)
                {
                    for (int k = 0; k < image.Depth; k++)
                        r1[k] = RowImage.data[i, j, k];
                    r2 = Find_NewValue(RowDetailImage, new double[2] { -1.0/3,-1.0/3 }, new int[2] { i, j }, 0);
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] += r2[k] *(-2.0/3);

                    FinalImage.SetPixel(2 * i+1, j, r1);


                    r1 = Find_NewValue(RowImage, new double[2] { 0.5, 0.5 }, new int[2] { i, j }, 0);
                    r2 = Find_NewValue(RowDetailImage, new double[3] { -1.0 / 6, 2.0 / 3, -1.0 / 6 }, new int[2] { i, j }, 0);
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] += (r2[k] /3.0);

                    FinalImage.SetPixel(2 * i + 2, j, r1);
                }
                //perfect reconstruction
                //fn=cn-2/3dn
                for (int k = 0; k < RowImage.Depth; k++)
                    r1[k] = RowImage.data[RowImage.Width - 1, j, k];


                for (int k = 0; k < image.Depth; k++)
                    r2[k] =(-2.0/3)* RowDetailImage.data[RowDetailImage.Width - 1, j, k];

                for (int k = 0; k < r2.Length; k++)
                    r1[k] += r2[k];
                FinalImage.SetPixel(FinalImage.Width - 1, j, r1);
              
            }
            //remove the used details
            if (dimage.TheDetails.Count > 0)
            {
                dimage.TheDetails.RemoveAt(dimage.TheDetails.Count - 1);
                dimage.TheDetails.RemoveAt(dimage.TheDetails.Count - 1);
                dimage.TheDetails.RemoveAt(dimage.TheDetails.Count - 1);
            }
            dimage.TheImage = FinalImage;
            // dimage.TheImage = RowDetailImage;
            return dimage;
        }
    }
}
